{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Python basics\n", "\n", "These Jupyter Notebooks to present Python code. Execute blocks of code with **ctrl+enter**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Reading:\n", "[Python tutorial](https://docs.python.org/3.7/tutorial/) 3.1, 4.1 - 4.7, 5.1 - 5.6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Variable types\n", "Python will infer the type when you define the variable. With `x = 2`, `x` is set to an **int**. With `x = 2.0`, `x` is set to a **float**.
\n", "`type(...)` displays a variable's type (useful for understanding the details of the code when debugging)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- **int**
\n", "A (signed) integer ...−2,−1,0,1,2,..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = 2\n", "print(type(x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- **float**
\n", "A real number with 16 digits of precision. (Equivalent to \"double\" in other languages. Python does not natively support single-precision floating point numbers.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y = 2.0\n", "print(type(y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- **str**
\n", "A string, a sequence of 0 or more characters. Enclosed within a pair of single quotes `'` or a pair of double quotes `\"`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = \"Hello World!\"\n", "print(type(a))\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Exercise__ : Read 3.1.2 in the [Python tutorial](https://docs.python.org/3.7/tutorial/introduction.html) and check the output of the following. (Double click the cell to see the actual code.)\n", "- print(\"McDonald's\")\n", "- print(\"McDonald\\'s\")\n", "- print('McDonald\\'s')\n", "- print('McDonald's')\n", "- print(r\"McDonald\\'s\")\n", "- print(3*\"McDonald\\'s\")\n", "- print(\"McDonald\" + \"\\'s\")\n", "- print(\"McDonald\" \"\\'s\")\n", "- print(\"\"\"McDonald\n", " \\'s\"\"\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"\"\"McDonald\n", " multiline string\n", " \\'s\"\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Indices refer to positions **between** characters. The left edge of the first character numbered **0**. Then the right edge of the last character of a string of length **n** characters has index **n**. \n", "\n", "
\n",
    " +---+---+---+---+---+---+\n",
    " | P | y | t | h | o | n |\n",
    " +---+---+---+---+---+---+\n",
    " 0   1   2   3   4   5   6\n",
    "-6  -5  -4  -3  -2  -1\n",
    "
\n", "\n", "Python also uses negative indexes.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To access a single element of `seq` immediately after index `ind`, use `seq[ind]`.\n", "\n", "__Exercise__: If `s = \"abcdefg\"`, what are the outputs to the following code?\n", "- print(s[0])\n", "- print(s[6])\n", "- print(s[7])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s = \"Python\"\n", "print(s[:])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The slice notation specifies two index positions separated by a colon (`:`) to access subsequences.
\n", "`seq[start:stop]` returns elements in `seq` between `start` and `stop`. If `start` or `stop` are omitted, they are set to the beginning or the end. If `stop` is larger than the end of the string, `stop` is set to the end of the string.\n", "\n", "__Exercise__: If `s = \"abcdefg\"`, what are the outputs to the following code?\n", "- print(s[0])\n", "- print(s[6])\n", "- print(s[7])\n", "- print(s[-1])\n", "- print(s[1:3])\n", "- print(s[:3])\n", "- print(s[3:])\n", "- print(s[:])\n", "- print(s[0:-2])\n", "- print(s[0:100])\n", "- s[0] = 'z'\n", "- s[0:3] = ['x','y','z']\n", "- print(len(s))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s[0] = 'z'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`seq[start:stop:step]` returns elements in `seq` between `start` and `stop` separated by `step`. (`step` is `1` by default.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s = 'Python'\n", "print(s[::2]) # print elements in even-numbered positions\n", "print(s[1::2]) # print elements in odd-numbered positions\n", "print(s[::-1]) # print elements in reverse order\n", "print(s[:-4:-1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comments\n", "Properly and adequately commenting is essential for ensuring your code is understandable by your colleagues and your future self. Make commenting a habit. (A wise man once said \"We first make our habits, and then our habits make us.\")
\n", "Python has two types of comments: long multi-line comments and short inline comments.\n", "\n", "`#` is the comment character: anything after `#` on the line is ignored. It is considered good style to use at least 2 blank spaces before an inline comment following code.\n", "\n", "Write **Multi-line comments** with multi-line strings, delimited by pairs of triple quotes (**`'''`** or **`\"\"\"`**)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# this is single-line comment\n", "print('Before comment') #It's good style to use at least 2 spaces here before the #\n", "'''\n", "This is\n", "a multi-line\n", "comment\n", "'''\n", "print('After comment')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lists\n", "A **list** is an ordered sequence of 0 or more comma-delimited elements enclosed within square brackets (`[`, `]`). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s = [1,2,3,4,5,6,7,8]\n", "print(s[2:5])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `+` to concatenate lists." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s = ['a','b','c','d','e']+['f','g']\n", "print(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Exercise:__ If `s = ['a','b','c','d','e'] + ['f','g']`, what are the outputs to the following code?\n", "- print(s[0])\n", "- print(s[6])\n", "- print(s[7])\n", "- print(s[-1])\n", "- print(s[1:3])\n", "- print(s[:3])\n", "- print(s[3:])\n", "- print(s[0:-2])\n", "- print(s[0:100])\n", "- s[0] = 'z'; print(s)\n", "- s[0:3] = ['x','y','z']; print(s)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s[0:3] = ['x','y','z']\n", "print(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lists may also have lists as their elements:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "K=[[1,2],[3,4,5],'s']\n", "print(K[0][1])\n", "print(K[0][:])\n", "print(K[1])\n", "print(K[2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The range function: \n", "Generates sequences of numbers in the form of a list. The provided end point is not included. (Actually, `range` creates an iterable object, not a list. More on this later.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(list(range(10)))\n", "print(list(range(1,10)))\n", "print(list(range(21,-1,-2)))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## If-else statement\n", "\n", "You can write if-else statements with
\n", "`if :`
\n", "      `code in if`
\n", "`elif :`
\n", "      `code in else-if`
\n", "`elif :`
\n", "      `code in else-if`
\n", "`else :`
\n", "      `code in else`
\n", "`code after if-else`\n", "\n", "The indentation for the if-else blocks is not optional." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = 0\n", "if x > 0:\n", " print(\"x is positive\")\n", "elif x < 0:\n", " print(\"x is negative\")\n", "else:\n", " print(\"x is 0\")\n", "\n", "print(\"Code after if-else statement\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## while loop\n", "\n", "\n", "You can write while loop with
\n", "`while :`
\n", "      `code in loop`
\n", "`code after loop`\n", "\n", "Again, indentation is not optional." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "var = 1\n", "while var != \"good bye\" :\n", " var = input(\"Say something :\")\n", " print(\"You entered: {}\".format(var))\n", "\n", "print(\"Good bye!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## for loop\n", "\n", "You can write for loops with
\n", "`for in :`
\n", "      `code in loop`
\n", "      `code in loop`
\n", "`code after loop`\n", "\n", "Again, indentation is not optional." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Print Fibonacci sequence\n", "F_prev = 0\n", "print(F_prev)\n", "F_curr = 1\n", "print(F_curr)\n", "\n", "#for loop with numerical index\n", "for i in range(10):\n", " F_next = F_prev + F_curr\n", " F_prev = F_curr\n", " F_curr = F_next\n", " print(F_next)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#for loop with list\n", "fruit_list = [\"apple\", \"banana\", \"cherry\"]\n", "for fruit in fruit_list:\n", " print(fruit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Generally, the for loop works with any \"iterable\" object. (More on this when we talk about objects.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#for loop with string\n", "for c in \"The Best Things in Life are Free\":\n", " print(\"Current character: {}\".format(c))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## List comprehension\n", "An intuitive and concise way to create lists." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ** power symbol\n", "[i**2 for i in range(6)]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[0 for _ in range(5)] #use _ if you don't need the variable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use **if conditionals** to filter out elements" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[i**2 for i in range(5) if i%2==0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can use **multiple for clauses**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[i+j for i in range(2) for j in range(4)]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[i+j for j in range(4) for i in range(2)] #What is the difference?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#For loop equivalent to the list comprehension\n", "L = []\n", "for i in range(2):\n", " for j in range(4):\n", " L.append(i+j)\n", "print(L)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[[i+j for i in range(2)] for j in range(4)]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[[i+j for j in range(4)] for i in range(2)] #what there a difference?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Exercise__ : Use a list comprehension to create: [[1,2,3],[2,4,6],[3,6,9],[4,8,12]]." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Exercise__ : Use a list comprehension to create:[0,0,0,0,1,2,0,2,4,0,3,6,0,4,8,0,5,10]." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Mutability\n", "One important distinction between strings and lists is their [**mutability**](https://docs.python.org/3.7/reference/datamodel.html).\n", "\n", "Strings are **immutable**, i.e., they cannot be modified. Methods that seemingly do modify a given string (like `str.strip()`) return modified **copies**.\n", "\n", "Lists are **mutable**, i.e., they can be modified. \n", "\n", "The following examples show `list` methods modifying lists." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_1 = [1, 2, 3, 5, 1]\n", "list_2 = list_1 # list_2 now references the same object as list_1\n", "\n", "print('list_1:', list_1)\n", "print('list_2:', list_2)\n", "\n", "list_1.remove(1) # remove [only] the first occurrence of 1 in list_1\n", "print('list_1:', list_1)\n", "print('list_2:', list_2)\n", "\n", "list_1.pop(2) # remove the element in position 2\n", "print('list_2:', list_2)\n", "\n", "list_1.append(6) # add 6 to the end of list_1\n", "print('list_1:', list_1)\n", "\n", "list_1.insert(0, 7) # add 7 to the beinning of list_1 (before the element in position 0)\n", "print('list_1:', list_1)\n", "\n", "list_1.sort()\n", "print('list_1:', list_1)\n", "\n", "list_1.reverse()\n", "print('list_1:', list_1)\n", "\n", "print('list_1:', list_1)\n", "print('list_2:', list_2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The slice notation creates __a copy of a list__. In fact, using `[:]` is the standard way to create copies of the entire list.\n", "\n", "If we assign that copy to another variable, the variables refer to different objects, so changes to one do not affect the other." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_1 = [1, 2, 3, 5, 1]\n", "list_2 = list_1[:] # list_1[:] returns a copy of the entire contents of list_1\n", "\n", "print('list_1:', list_1)\n", "print('list_2:', list_2)\n", "\n", "list_1.remove(1) # remove [only] the first occurrence of 1 in list_1\n", "print('list_1:', list_1)\n", "print('list_2:', list_2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dictionary\n", "A dictionary is a set of __keys__ each pointing to a __value__. The keys must be unique, but values may be repeated." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#d contains names and GPA key-value pairs. Duplicate names are not allowed, but two people can have the same GPA.\n", "d = {'Alice':4.23, 'Bob':4.1, 'Charlie': 3.8, 'Daniel':3.8}\n", "print(d['Alice'])\n", "print(d.keys())\n", "print(d.values())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Modules\n", "Modules are packages with classes and functions. You `import` a module to use it.
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random #import random module\n", "\n", "#We can now use functions from the random module\n", "print(random.random()) #uniform real in [0,1]\n", "print(random.randint(1,3)) #uniform integer in {1,2,3}\n", "L=[3,4,5]\n", "random.shuffle(L) #uniform shuffle of a list\n", "print(L)\n", "print(random.sample(L,2)) #uniform sampling" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you plan to use a module often, shorten the name with `as`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random as rnd\n", "rnd.random()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can import everything from a module and use the functions directly without naming the module. I think this is bad practice because we completely lose track of where the functions come from and, more importantly, the risk of name conflicts increase." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from random import * #this is bad\n", "from math import *\n", "\n", "print(random()) #from which module does random come from?\n", "log(5) #Function logs (keeps record of) the number 5. (Just kidding. It computes the natural logarithm.) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The previous code block did some bad things (for demonstration purposes). Let's clear everything with `%reset`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Import specific features from a module with `from module import ` and later use it without referring to the module name. This adds convenience without being reckless as `from module import *`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from numpy.linalg import eigvals #specifically import eigvals\n", "\n", "A = np.matrix([[0, 0, 0, 0, 30/8],\n", " [1, 0, 0, 0, -67/8],\n", " [0, 1, 0, 0, -13/8],\n", " [0, 0, 1, 0, 54/8],\n", " [0, 0, 0, 1, 4/8]])\n", "\n", "\n", "# print(np.linalg.eigvals(A)) # function call is a bit long and cumbersome\n", "print(eigvals(A)) # we can call eigvals without referring to the module name" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Variables\n", "\n", "In Python, **variables** are **names** that refer to **things** (such as objects).\n", "\n", "\"Changing something\" can mean:\n", " - changing what the name refers to (using `=`) or\n", " - mutating the thing being referred to (using a member function).\n", "\n", "In the following example, we use `id(obj)`, which returns a unique identifier of `obj`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Variable is made to refer to something else\n", "def f(x):\n", " print(\"x=\",x,\" id=\",id(x))\n", " x = 42\n", " print(\"x=\",x,\" id=\",id(x))\n", "\n", "x = 5\n", "print(x)\n", "print(id(x))\n", "f(x)\n", "print(x)\n", "print(id(x))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Variable refers to the same but mutated list \n", "def f(lst):\n", " print(\"lst=\",lst,\" id=\",id(lst))\n", " lst.append(5)\n", " print(\"lst=\",lst,\" id=\",id(lst))\n", "\n", "lst = ['a']\n", "print(lst)\n", "print(id(lst))\n", "f(lst)\n", "print(lst)\n", "print(id(lst))\n", "\n", "#Aside: don't use 'list' as a variable name since it is a reserved Python keyword" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dintinguishing a variable from the object it refers to is essential.\n", "\n", "In the code `x=[1,2,3]`:\n", " - the right-hand-side `[1,2,3]` creates a list object and \n", " - `x=` enters `x` into the \"symbol table\" (a table of recognized names) and makes `x` refer to the list object.\n", " \n", "\n", "In the following example, `x == y` asks whether `x` and `y` represent the \"same\" thing while `x is y` asks whether `x` and `y` refer to the same object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = [1,2,3]\n", "y = [1,2,3]\n", "z = x\n", "\n", "print(x is y)\n", "print(x == y)\n", "print(x is z)\n", "print(x == z)\n", "print(y is z)\n", "print(y == z)\n", "\n", "x.append(4)\n", "print(x)\n", "print(y)\n", "print(z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Using `None`, a variable can be made to refer to nothing. A variable referring to `None` is not the same as a variable not exiting." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset\n", "x = None\n", "print(x is None)\n", "\n", "x = 6\n", "print(x is None)\n", "\n", "print(y is None) #Error! y does not exist, so we cannot ask if it refers to anything." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions\n", "A function is a block of code that runs when it is called. A function takes 0,1,2,... inputs and returns 0 or 1 result. A function can effectively return multiple objects by returning a list or a tuple." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Define a function\n", "def nplusone(n):\n", " m = n + 1\n", " return m\n", "\n", "def isbig(n):\n", " if n > 10:\n", " return True\n", " else:\n", " return False\n", " \n", "#Call a function\n", "nplusone(6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Exercise__ : Write a function `duplicates(lst)` that returns a list of all elements appearing twice\n", "or more in the input list `lst`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, indentation is part of the formal syntax in Python and therefore is not optional." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Error! Incorrect indentation!\n", "def nplusone(n):\n", "m = n + 1\n", "return m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is important to remember that mutable function inputs can be changed inside the function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def f(x):\n", " x[1] = 1000\n", " return x\n", " \n", "def g(x):\n", " y = x[:] # creates a copy \n", " y[1] = 1000\n", " return y" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = [1, 2, 3]\n", "print(\"Initially, a was\", a)\n", "f(a)\n", "print(\"Now, a is \",a)\n", "\n", "b = [1, 2, 3]\n", "print(\"Initially, b was\", b)\n", "c = g(b)\n", "print(\"b is still\",b)\n", "print(\"c is\",c)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d = {'A':1, 'B':2}\n", "print(\"Initially, d was\", d)\n", "f(d)\n", "print(\"Now, d is\", d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Within functions, you have separate variable names." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Change within function not visible from outside\n", "def f(x):\n", " x = 42 #x refers to something else, but the int object representing 5 is still 5\n", "\n", "x = 5\n", "print(x)\n", "f(x)\n", "print(x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Same program. The name of the function input y is separate from the\n", "#name of x which was passed into the function.\n", "def f(y):\n", " y = 42\n", "\n", "x = 5\n", "print(x)\n", "f(x)\n", "print(x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Change within function visible from outside\n", "def f(lst):\n", " lst.append(5) #the list lst refers to is changed\n", "\n", "lst = ['a']\n", "print(lst)\n", "f(lst)\n", "print(lst)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions with default argument values\n", "Consider the function" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def my_fun(a, b = 10, c = 20):\n", " print(a, b, c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Predict the output of the following:\n", "- my_fun( )\n", "- my_fun(1)\n", "- my_fun(1,2)\n", "- my_fun(1,2,3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_fun(1,2,3)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Important:\n", "The default value for a function argument is only evaluated once, at the time that the function is defined. \n", "\n", "__Common mistake:__ misusing mutable default arguments" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def foo(bar=[]): # bar is optional and defaults to [] if not specified\n", " bar.append(\"SNU\") # but this line could be problematic, as we'll see...\n", " return bar" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo([])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To fix this, we can do" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def foo(bar = None):\n", " if bar is None:\n", " bar = []\n", " bar.append(\"SNU\")\n", " return bar" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Another common mistake**: setting the current time as the default argument." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from datetime import datetime\n", "\n", "def printTime(currentTime = datetime.now()):\n", " print(\"The current time is \" + str(currentTime))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "printTime()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "printTime()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Question: how would you fix `printTime`?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Keyword Arguments:\n", "Functions can also be called using **keyword arguments** of the form kwarg=value. (Normal arguments are called **positional** arguments.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def parrot(voltage, state = 'a stiff', action = 'voom', type = 'Norwegian Blue'):\n", " print(\"-- This parrot wouldn't\", action)\n", " print(\"if you put\", voltage, \"volts through it.\")\n", " print(\"-- Lovely plumage, the\", type)\n", " print(\"-- It's\", state, \"!\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "parrot(1000) # 1 positional argument\n", "parrot(voltage = 1000) # 1 keyword argument\n", "parrot(voltage = 1000000, action = 'VOOOOOM') # 2 keyword arguments\n", "parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments\n", "parrot('a million', 'bereft of life', 'jump') # 3 positional arguments\n", "parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "parrot(action='BAAM', 1000) #ERROR! keyword arguments must come after positional arguments\n", "parrot(1000, foo=3) #ERROR! Keyword arguments must match one of the argumens" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise:\n", "Read 4.7.3 and write a function __count_args__ that accepts any number of input arguments and returns the number of arguments it received, e.g. count_args(10,2,3,1) returns 4 and count_args([10,2,3,1]) returns 1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Unpacking argument list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can unpack a list or tuple for a function with *." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def fn(a, b, c):\n", " print(a + b + c)\n", " \n", "fn(1, 2, 3)\n", "\n", "lst = [\"we\", \"love\", \"you\"]\n", "# fn(lst) #fail\n", "fn(*lst)\n", "\n", "tpl = (1, 2, 7)\n", "fn(*tpl)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can use a dictionary to deliver keyword arguments for a function with **." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def parrot(voltage, state, action):\n", " print(\"-- This parrot wouldn't\", action)\n", " print(\"if you put\", voltage, \"volts through it.\")\n", " print(\"E's\", state, \"!\")\n", "\n", "d = {\"state\": \"bleedin' demised\", \"action\": \"VOOM\", \"voltage\": \"four million\"}\n", "parrot(**d)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Tuples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Tuples__ are immutable, and usually contain a heterogeneous sequence of elements that are accessed via unpacking or indexing. \n", "__Lists__ are mutable, and their elements are usually homogeneous and are accessed by iterating over the list." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = (3,'a',[1,2,3],{'A':1, 'B':2})\n", "a,b,c,d = x # tuple unpacking\n", "print(b)\n", "print(x[2])\n", "print(d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Multiple assignment\n", "Using tuples, you can conveniently assign multiple variables in one line. This makes your code more concise and readable." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a,b = 1,2 #Multiple assignment without paratheses\n", "(c,d) = (4,\"hello\") #Multiple assignment with paratheses" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although tuples are immutable, they can contain mutable objects" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list1 = ['a','b']\n", "pair1 = (3,list1)\n", "list1.append('c')\n", "print(pair1)\n", "#pair1 did not change. It contains the same object references\n", "#the same list pair1 referes to changed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Empty tuple and 1-tuple__\n", "\n", "You can have tuples of length 0 and 1" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tpl = () #tuple of length 0\n", "print(tpl)\n", "\n", "tpl = (1,) #tuple of length 1\n", "print(tpl)\n", "\n", "tpl = 1, #tuple of length 1 (without parentheses)\n", "print(tpl)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sets\n", "A set is an unordered collection of items. Every element is unique (no duplicates) and must be immutable. However, the set itself is mutable. We can add or remove items from it.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_set = {1,2,3,4,3,2}\n", "\n", "print(my_set)\n", "\n", "set2 = set()\n", "print(set2)\n", "\n", "empty_dict = {}\n", "print(empty_dict)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# my_set = {1, 2, [3,4]} # error! set cannot have mutable items\n", "# my_set[0] # error! set does not support indexing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Try the following methods to change a set in Python:__\n", "- my_set = {1,2,3}\n", "- my_set.add(4) # add one item\n", "- my_set.update([5,6,7]) #add multiple items" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_set = {1,2,3}\n", "my_set.add(4)\n", "my_set.update([5,6,7])\n", "print(my_set)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Exercise__: Determine the number of unique letters in \"supercalifragilisticexpialidocious\" using a set." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s = \"supercalifragilisticexpialidocious\"\n", "st = set()\n", "for c in s:\n", " st.add(c)\n", "print(len(st))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# String formatting\n", "\n", "String formatting is incredibly an incredibly convenient tool." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "person = {\"name\": \"Alice\", \"age\" : 20}\n", "\n", "# Cumbersome to write\n", "print('My name is '+ person[\"name\"] + ' and I am ' + str(person[\"age\"]) + ' years old.')\n", "\n", "# Much easier to write and read\n", "print('My name is {} and I am {} years old.'.format(person[\"name\"],person[\"age\"]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are many detailed features to string formatting. The following is just a few.\n", "\n", "## str.format\n", "str.format() was introduced in Python 2.6\n", "\n", "For more information, see\n", "https://docs.python.org/3.7/library/string.html#formatstrings" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "person = {\"name\": \"Alice\", \"age\" : 20}\n", "\n", "#Use numbers to refer to positional arguments\n", "print('My name is {0} and I am {1} years old.'.format(person[\"name\"],person[\"age\"]))\n", "print('I am {1} years old and my name is {0}.'.format(person[\"name\"],person[\"age\"]))\n", "\n", "#You can use keyword arguments\n", "print('My phone number is : {areaCode}{sep}{centralCode}{sep}{stationCode}'.format(areaCode=310, centralCode=111, stationCode=312, sep=\"-\"))\n", "\n", "#You can access list and dictionary entries with [..]\n", "print('My name is {0[name]} and I am {0[age]} years old.'.format(person))\n", "\n", "import math\n", "\n", "radius = 2.2\n", "print('The circle with radius {} has area {:.2f}'.format(radius, radius**2*math.pi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## f-strings\n", "f-string is a string manipulation tool introduced in Python 3.6.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "name = \"Eric\"\n", "age = 74\n", "print(f\"Hello, {name}. You are {age} years old.\")\n", "\n", "\n", "\n", "weight = 150\n", "print(f\"{name} weighs {weight} pounds.\")\n", "print(F\"{name} weighs {weight*0.45359237} kilograms.\")\n", "print(f\"{name} weighs {weight*0.45359237:.2f} kilograms.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Useful built-in functions for manipulating sequence elements\n", "\n", " - enumerate\n", " - zip\n", " - reversed\n", " - items\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`enumerate` adds a counter to an iterable." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "l = ['tic', 'tac', 'toe']\n", "\n", "for i,v in enumerate(l,1):\n", " print(i,v)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`zip` combines lists (and iterables) into an iterable of tuples." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "questions = ['name', 'quest', 'favorite color']\n", "answers = ['lancelot', 'the holy grail', 'blue']\n", "\n", "\n", "for (q, a) in zip(questions, answers):\n", " print('What is your {0}? It is {1}.'.format(q, a))\n", " \n", "for q, a in zip(questions, answers): # explicit paratheses (..) can be omitted\n", " print('What is your {0}? It is {1}.'.format(q, a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`reversed` reverses an iterable." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "l = list(range(1,10,2))\n", "for i in reversed(l):\n", " print (i)\n", "\n", "# you can achieve the same effect with slice indexing\n", "for i in l[::-1]:\n", " print (i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`items` returns an iterable containing key value pairs of a dictionary." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "month_name = {1: 'Jan', 2: 'Feb', 3:'Mar'}\n", "for k, v in month_name.items():\n", " print(k, v)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The __del__ statement\n", "`del` removed variables and elements of lists" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s = \"hello\"\n", "del s\n", "# print(s) # reference variable deletes\n", "\n", "a = list(range(5))\n", "print(a)\n", "\n", "del a[2]\n", "print(a)\n", "\n", "del a[1:3]\n", "print(a)\n", "\n", "del a[:]\n", "print(a)\n", "\n", "del a\n", "# print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More precisely, `del` deletes the reference variable, not necessarily the object itself." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = \"hello\"\n", "B = A\n", "del A\n", "print(B) #No error. A is deleted but the object \"hello\" still exists as it is still referenced by B" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 4 }